package org.essilab.analyzer.wikitionary.tags;

import java.io.FileReader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.essilab.analyzer.analyzer.Term;

/**
 * This class is used to parse the text content of a word and create a Term with
 * the found tags and their values.
 *
 * @author Mohamed-Amine Chouchene, Ahcène Idinarere, Vincent Nock, Alain
 * Janin-Manificat
 */
public class FindTag {

	private static final Properties PROPS = new Properties();
	private static final Properties TAGS = new Properties();
	private static final Properties RELATIONS = new Properties();

	static {
		try {
			PROPS.load(new FileReader("types.txt"));
			TAGS.load(new FileReader("relations.txt"));
			RELATIONS.load(new FileReader("relation_type.txt"));
		} catch (Exception ex) {
			Logger.getLogger(FindTag.class.getName()).log(Level.SEVERE, null, ex);
		}

	}

	/**
	 * Tell whether or not the given key exist in property file FindTag#PROPS.
	 *
	 * @param tag String - the key to look for in FindTag#PROPS.
	 * @return boolean - True if the key exists false otherwise.
	 */
	private static boolean contains(String tag) {
		if (null != tag && !tag.trim().isEmpty()) {
			return PROPS.containsKey(tag);
		}
		return false;
	}

	/**
	 * Parse the text of a term and set properties of Term object. Set the type
	 * and the relations type list (for flex).
	 *
	 * @param term Term - The Term object where properties will be set.
	 * @param tag String - the tag to look for in given text.
	 * @param text String- the text to parse.
	 * @return boolean - Tell whether or not the tag has been found in the text.
	 */
	public static boolean addType(Term term, String tag, String text) {
		if (null == tag || tag.trim().isEmpty()) {
			return false;
		}
		String tmp = extractTag(tag);
		if (contains(tmp)) {
			term.setType(PROPS.getProperty(tmp));
			if (RELATIONS.containsKey(tmp)) {
				List<String> rels = extractRelationType(text);
				setTermValue(term, RELATIONS.getProperty(tmp), rels);
			}
			return true;
		} else {
			return false;
		}
	}

	/**
	 * Extract the relation types - the words between [[text]] - in the given
	 * text.
	 *
	 * @param text String - the text to parse.
	 * @return List<String> - the list of relation.
	 */
	private static List<String> extractRelationType(String text) {
		if (null != text && !text.trim().isEmpty()) {
			String[] lines = text.split("\n");
			Matcher m = null;
			List<String> rels = new ArrayList<>();
			for (String string : lines) {
				if (string.trim().startsWith("#")) {
					m = p.matcher(string);
					while (m.find()) {
						String att = m.group().trim();
						if (att.contains("[[")) {
							att = att.replace("[[", "");
						}
						if (att.contains("]]")) {
							att = att.replace("]]", "");
						}
						if (att.contains("[|]")) {
							att = att.split("[|]")[0];
						}
						if(!rels.contains(att.trim()))
							rels.add(att);
					}
				}
			}
			return rels;
		}
		return null;
	}

	/**
	 * Extract the tag label from the given string.
	 *
	 * @param tag String - a string containing a tag.
	 * @return String - the tag label contained in the given string.
	 */
	private static String extractTag(String tag) {
		String tmp = tag.replace("{{", "").replace("}}", "").trim();
		if (tmp.contains("|")) {
			tmp = tmp.split("[|]")[0];
		}
		if (tmp.startsWith("-")) {
			tmp = tmp.substring(1).trim();
		}
		if (tmp.endsWith("-")) {
			tmp = tmp.substring(0, tmp.length() - 1).trim();
		}
		return tmp;
	}

	/**
	 * Add relations to a Term. Parse the given text to find the given tag.
	 *
	 * @param term Term - The Term object where properties will be set.
	 * @param tag String - the tag to look for in given text.
	 * @param text String- the text to parse.
	 */
	public static void addRelation(Term term, String tag, String text) {
		if (null == tag || tag.trim().isEmpty()) {
			return;
		}
		String tmp = extractTag(tag);
		if (TAGS.containsKey(tmp.toLowerCase())) {
			List<String> rels = extractRelation(text);
			setTermValue(term, TAGS.getProperty(tmp), rels);
		} else {
			if (tmp.toLowerCase().equals("trad") && null != text && !text.trim().isEmpty()) {
				Map<String, List<String>> trans = extractTranslation(text);
				term.setTranslations(trans);
			}
		}
	}

	private static Pattern p = Pattern.compile("\\[\\[.*?\\]\\]", Pattern.CASE_INSENSITIVE);

	private static List<String> extractRelation(String text) {
		List<String> relations = new ArrayList<>();
		if (null != text && !text.trim().isEmpty()) {
			String[] lines = text.split("\n");
			Matcher m = null;
			for (String string : lines) {
				if (string.trim().startsWith("*")) {
					m = p.matcher(string);
					while (m.find()) {
						String att = m.group().replace("[[", "").replace("]]", "").trim();
						if (att.contains("|")) {
							att = att.split("[|]")[0];
						}
						if (!att.trim().isEmpty()) {
							relations.add(att);
						}
					}
				}
			}
		}
		return relations;
	}

	/**
	 * Use reflection to call the method identified by tmp.
	 *
	 * @param term Term - the object used to call the method.
	 * @param tmp String - the method name to call.
	 * @param value List<String>- the parameter to use with the method.
	 */
	private static void setTermValue(Term term, String tmp, List<String> value) {
		try {
			Method m = term.getClass().getMethod(tmp.trim(), new Class[]{List.class});
			m.invoke(term, new Object[]{value});
		} catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
			e.printStackTrace();
		}
	}
	private static Pattern tran = Pattern.compile("\\{\\{.*?\\}\\}", Pattern.CASE_INSENSITIVE);

	/**
	 * Extract the translations in the given text.
	 *
	 * @param text String - the text to parse.
	 * @return Map<String, List<String>> - Key : the language Value : the list
	 * of translations.
	 */
	public static Map<String, List<String>> extractTranslation(String text) {
		if (null != text && !text.trim().isEmpty()) {
			String[] lines = text.split("\n");
			Map<String, List<String>> keys = new HashMap<>();
			for (String string : lines) {
				string = string.trim();
				if (string.contains("{{trad-trier}}")) {
					break;
				}
				if (string.startsWith("*")) {
					if (string.contains(":")) {
						String[] tmp = string.split(":");
						if (tmp.length > 1) {
							string = tmp[1];
						} else {
							string = tmp[0];
						}
						if (null != string && !string.trim().isEmpty()) {
							Matcher m = tran.matcher(string);
							while (m.find()) {
								String att = m.group();
								if (att.contains("|")) {
									if (att.contains("{{")) {
										att = att.replace("{{", "");
									}
									if (att.contains("}}")) {
										att = att.replace("}}", "");
									}
									String[] atts = att.split("[|]");
									if (atts.length > 2) {
										String lang = atts[1];
										String term = atts[2];
										if (keys.containsKey(lang)) {
											List<String> values = keys.get(lang);
											if(!values.contains(term))
												values.add(term);
										} else {
											List<String> values = new ArrayList<>();
											values.add(term);
											keys.put(lang, values);
										}
									}
								}
							}
						}
					}
				}
			}
			return keys;
		}
		return null;
	}
}
